package com.meilingcloud.activiti.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.UUID;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.meilingcloud.activiti.domain.ActWfShift;
import com.meilingcloud.activiti.domain.dto.ActWfShiftSaveDto;
import com.meilingcloud.activiti.domain.vo.ActShiftDetailVo;
import com.meilingcloud.activiti.mapper.ActWfShiftMapper;
import com.meilingcloud.activiti.service.IActWfShiftService;
import com.meilingcloud.biobank.domain.*;
import com.meilingcloud.biobank.service.*;
import com.meilingcloud.common.config.BimsConfig;
import com.meilingcloud.common.constant.Constants;
import com.meilingcloud.common.core.domain.entity.SysUser;
import com.meilingcloud.common.enums.ShiftType;
import com.meilingcloud.common.enums.StockState;
import com.meilingcloud.common.enums.StockStatus;
import com.meilingcloud.common.exception.CustomException;
import com.meilingcloud.common.utils.SecurityUtils;
import com.meilingcloud.system.service.ISysConfigService;
import com.meilingcloud.system.service.ISysUserService;
import lombok.RequiredArgsConstructor;
import org.activiti.api.process.model.ProcessInstance;
import org.activiti.api.process.model.builders.ProcessPayloadBuilder;
import org.activiti.api.process.runtime.ProcessRuntime;
import org.activiti.engine.RuntimeService;
import org.activiti.engine.TaskService;
import org.activiti.engine.task.Task;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.messaging.simp.SimpMessagingTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.*;
import java.util.stream.Collectors;

/**
 * 转移单据 Service业务层处理
 *
 * @author chenl
 * @date 2021-04-30
 */
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Service
public class ActWfShiftServiceImpl extends ServiceImpl<ActWfShiftMapper, ActWfShift> implements IActWfShiftService {

    private final ProcessRuntime processRuntime;
    private final TaskService taskService;
    private final RuntimeService runtimeService;
    private final ISysConfigService sysConfigService;
    private final ISysUserService sysUserService;
    private final ITubeService tubeService;
    private final IContainerService containerService;
    private final ISampleBackupService sampleBackupService;
    private final IShiftContainerService shiftContainerService;
    private final IShiftSampleService shiftSampleService;
    private final SimpMessagingTemplate simpMessagingTemplate;

    @Override
    public List<ActWfShift> queryList(ActWfShift actWfShift) {
        LambdaQueryWrapper<ActWfShift> lqw = Wrappers.lambdaQuery();
        if (StringUtils.isNotBlank(actWfShift.getShiftType())) {
            lqw.eq(ActWfShift::getShiftType, actWfShift.getShiftType());
        }
        if (StringUtils.isNotBlank(actWfShift.getShiftTitle())) {
            lqw.eq(ActWfShift::getShiftTitle, actWfShift.getShiftTitle());
        }
        if (StringUtils.isNotBlank(actWfShift.getShiftDesc())) {
            lqw.eq(ActWfShift::getShiftDesc, actWfShift.getShiftDesc());
        }
        if (StringUtils.isNotBlank(actWfShift.getInstanceId())) {
            lqw.eq(ActWfShift::getInstanceId, actWfShift.getInstanceId());
        }
        if (StringUtils.isNotBlank(actWfShift.getShiftState())) {
            lqw.eq(ActWfShift::getShiftState, actWfShift.getShiftState());
        }
        if (StringUtils.isNotBlank(actWfShift.getCreateBy())) {
            lqw.eq(ActWfShift::getCreateBy, actWfShift.getCreateBy());
        }
        return this.list(lqw);
    }

    @Override
    public List<ActWfShift> queryShiftWithTaskList(ActWfShift actWfShift) {
        List<ActWfShift> actWfShifts = this.queryList(actWfShift);
        List<String> instanceIds = actWfShifts.stream().map(ActWfShift::getInstanceId).collect(Collectors.toList());
        if (ObjectUtil.isNotEmpty(instanceIds)) {
            List<Task> tasks = taskService.createTaskQuery().processInstanceIdIn(instanceIds).list();
            actWfShifts.forEach(item -> {
                Task task = tasks.stream().filter(t -> t.getProcessInstanceId().equals(item.getInstanceId())).findAny().orElse(null);
                if (ObjectUtil.isNotNull(task)) {
                    item.setTaskName(task.getName());
                }
            });
        }
        return actWfShifts;
    }

    @Override
    public List<ActWfShift> queryShiftInDeptList(ActWfShift actWfShift, List<String> userNames) {
        if (CollectionUtil.isEmpty(userNames)) {
            return null;
        }
        LambdaQueryWrapper<ActWfShift> lqw = Wrappers.lambdaQuery();
        if (StringUtils.isNotBlank(actWfShift.getShiftType())) {
            lqw.eq(ActWfShift::getShiftType, actWfShift.getShiftType());
        }
        if (StringUtils.isNotBlank(actWfShift.getShiftTitle())) {
            lqw.eq(ActWfShift::getShiftTitle, actWfShift.getShiftTitle());
        }
        if (StringUtils.isNotBlank(actWfShift.getShiftState())) {
            lqw.eq(ActWfShift::getShiftState, actWfShift.getShiftState());
        }
        lqw.in(ActWfShift::getCreateBy, userNames);
        return this.list(lqw);
    }

    @Override
    public List<String> queryUserByDept() {
        SysUser sysUser = new SysUser();
        sysUser.setDeptId(SecurityUtils.getLoginUser().getUser().getDeptId());
        List<SysUser> sysUsers = sysUserService.selectUserList(sysUser);
        List<String> userNames = sysUsers.stream().map(SysUser::getUserName).collect(Collectors.toList());
        return userNames;
    }

    @Override
    public ActShiftDetailVo getShiftDetail(String shiftId) {
        ActWfShift actWfShift = this.getById(shiftId);
        if (ObjectUtil.isEmpty(actWfShift)) {
            return null;
        }
        ActShiftDetailVo actShiftDetailVo = new ActShiftDetailVo();
        BeanUtils.copyProperties(actWfShift, actShiftDetailVo);
        ShiftType shiftType = ShiftType.getTypeByCode(actWfShift.getShiftType());
        switch (shiftType) {
            case CONTAINER:
                List<ShiftContainer> shiftContainers = shiftContainerService.list(new LambdaQueryWrapper<ShiftContainer>()
                        .eq(ShiftContainer::getShiftId, shiftId));
                actShiftDetailVo.setShiftContainers(shiftContainers);
                break;
            case SAMPLE:
                List<ShiftSample> shiftSamples = shiftSampleService.list(new LambdaQueryWrapper<ShiftSample>()
                        .eq(ShiftSample::getShiftId, shiftId));
                actShiftDetailVo.setShiftSamples(shiftSamples);
                break;
            default:
                break;
        }
        return actShiftDetailVo;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean revoke(String businessKey) {
        org.activiti.engine.runtime.ProcessInstance processInstance = runtimeService.createProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey).includeProcessVariables().singleResult();
        if (ObjectUtil.isEmpty(processInstance)) {
            throw new CustomException("此流程已结束");
        }
        runtimeService.deleteProcessInstance(processInstance.getId(), "用户撤销");
        // 删除流程历史信息
//        historyService.deleteHistoricProcessInstance(processInstance.getId());
        // 更改业务表状态为撤销
        ActWfShift actWfShift = this.getById(businessKey);
        actWfShift.setShiftState(StockStatus.REVOKE.getCode());
        this.updateById(actWfShift);
        // 更改样本复份流程状态
        List<ShiftSample> shiftSamples = shiftSampleService.list(new LambdaQueryWrapper<ShiftSample>()
                .eq(ShiftSample::getShiftId, businessKey));
        List<Long> sampBackupIds = shiftSamples.stream().map(ShiftSample::getSampBackupId).collect(Collectors.toList());
        List<SampleBackup> sampleBackupList = sampleBackupService.listByIds(sampBackupIds);
        List<SampleBackup> sampleBackups = sampleBackupList.stream().map(item -> {
            item.setStockState(StockState.NORMAL.getCode());
            return item;
        }).collect(Collectors.toList());
        if (CollectionUtil.isNotEmpty(sampleBackups)) {
            return sampleBackupService.updateBatchById(sampleBackups);
        }
        return false;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean saveWfShift(ActWfShiftSaveDto actWfShiftSaveDto) {
        ActWfShift actWfShift = new ActWfShift();
        BeanUtils.copyProperties(actWfShiftSaveDto, actWfShift);
        String id = UUID.randomUUID().toString();
        actWfShift.setShiftId(id);
        // 是否启用审批
        String activitiEnable = sysConfigService.selectConfigByKey("activiti.enable");
        boolean result = false;
        if (!StringUtils.equals(activitiEnable, Constants.ACTIVITI_ENABLE)) {
            result = saveShiftByActivitiEnable("", actWfShift, actWfShiftSaveDto);
        }

        // 候选用户配置 ${deptLeader} or 候选组配置 se
        String deptLeaders = StringUtils.join(sysUserService.selectUserNameByPostCodeAndDeptId(BimsConfig.getPostCode(),
                SecurityUtils.getLoginUser().getUser().getDeptId()), ",");
        Map<String, Object> variableMap = new HashMap<>();
        variableMap.put("enable", activitiEnable);
        variableMap.put("deptLeader", deptLeaders);
        // 启动实例
        ProcessInstance processInstance = processRuntime.start(ProcessPayloadBuilder
                .start()
                .withProcessDefinitionKey("shift")
                .withName(actWfShift.getShiftTitle())
                .withBusinessKey(id)
                .withVariables(variableMap)
                .build());
        // WebSocket通知
        List<String> deptLeaderList = Arrays.asList(deptLeaders.split(","));
        if (CollectionUtil.isNotEmpty(deptLeaderList)) {
            deptLeaderList.forEach(item -> {
                simpMessagingTemplate.convertAndSendToUser(item, "/topic/activiti", "您收到 " + actWfShiftSaveDto.getShiftTitle());
            });
        }
        if (StringUtils.equals(activitiEnable, Constants.ACTIVITI_ENABLE)) {
            result = saveShiftByActivitiEnable(processInstance.getId(), actWfShift, actWfShiftSaveDto);
        }

        return result;
    }

    // 根据是否需要activiti审批设置 保存业务数据时间节点不同
    @Transactional(rollbackFor = Exception.class)
    public boolean saveShiftByActivitiEnable(String processInstanceId, ActWfShift actWfShift,
                                             ActWfShiftSaveDto actWfShiftSaveDto) {
        if (StringUtils.isNotBlank(processInstanceId)) {
            actWfShift.setInstanceId(processInstanceId);
        }
        // 业务表写入数据
        actWfShift.setShiftState(StockStatus.UNDERWAY.getCode());
        actWfShift.setCreateName(SecurityUtils.getNickName());
        actWfShift.setCreateBy(SecurityUtils.getUsername());
        boolean result = this.save(actWfShift);
        ShiftType shiftType = ShiftType.getTypeByCode(actWfShift.getShiftType());
        switch (shiftType) {
            case CONTAINER: // 转移容器
                List<ShiftContainer> shiftContainers = actWfShiftSaveDto.getShiftContainers().stream().map(item -> {
                    item.setShiftId(actWfShift.getShiftId());
                    return item;
                }).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(shiftContainers)) {
                    shiftContainerService.saveBatch(shiftContainers);
                }
                break;
            case SAMPLE: // 转移样本
                List<ShiftSample> shiftSamples = actWfShiftSaveDto.getShiftSamples().stream().map(item -> {
                    item.setShiftId(actWfShift.getShiftId());
                    return item;
                }).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(shiftSamples)) {
                    shiftSampleService.saveBatch(shiftSamples);
                }
                // 更新样本库存状态为流程状态
                List<Long> sampBackupIds = shiftSamples.stream().map(ShiftSample::getSampBackupId).collect(Collectors.toList());
                List<SampleBackup> sampleBackupList = sampleBackupService.listByIds(sampBackupIds);
                List<SampleBackup> sampleBackups = sampleBackupList.stream().map(item -> {
                    item.setStockState(StockState.PROCESS.getCode());
                    return item;
                }).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(sampleBackups)) {
                    sampleBackupService.updateBatchById(sampleBackups);
                }
                break;
            default:
                break;
        }
        return result;
    }

    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean completeShift(ActWfShift wfShift) {
        ActWfShift actWfShift = this.getById(wfShift.getShiftId());
        actWfShift.setShiftState(wfShift.getShiftState());
        actWfShift.setInstanceId(wfShift.getInstanceId());
        ShiftType shiftType = ShiftType.getTypeByCode(actWfShift.getShiftType());
        boolean wfSuccess = StockStatus.SUCCESS.getCode().equals(actWfShift.getShiftState()) ||
                StockStatus.AUTO.getCode().equals(actWfShift.getShiftState());
        boolean wfFail = StockStatus.FAIL.getCode().equals(actWfShift.getShiftState());
        switch (shiftType) {
            case CONTAINER:
                if (wfSuccess) {
                    // 重新计算存储数量
                    List<ShiftContainer> shiftContainers = shiftContainerService.list(new LambdaQueryWrapper<ShiftContainer>()
                            .eq(ShiftContainer::getShiftId, actWfShift.getShiftId()));
                    List<Container> toUpdateContainers = new ArrayList<>();
                    shiftContainers.forEach(item -> {
                        Container container = containerService.getById(item.getContainerId());
                        container.setContainerLocation(item.getNewContainerLocation());
                        container.setParentId(item.getNewParentId());
                        toUpdateContainers.add(container);

                        List<Container> oldContainers = containerService.getAllParentsByChild(item.getOldParentId());
                        List<Container> oldContainerList = oldContainers.stream().map(j -> {
                            j.setCurrentCount(j.getCurrentCount() - item.getCurrentCount()); // 更改数量
                            j.setTotalCount(j.getTotalCount() - item.getTotalCount());
                            return j;
                        }).collect(Collectors.toList());
                        toUpdateContainers.addAll(oldContainerList);

                        List<Container> newContainers = containerService.getAllParentsByChild(item.getNewParentId());
                        List<Container> newContainerList = newContainers.stream().map(k -> {
                            k.setCurrentCount(k.getCurrentCount() + item.getCurrentCount()); // 更改数量
                            k.setTotalCount(k.getTotalCount() + item.getTotalCount());
                            return k;
                        }).collect(Collectors.toList());
                        toUpdateContainers.addAll(newContainerList);
                    });
                    if (CollectionUtil.isNotEmpty(toUpdateContainers)) {
                        containerService.updateContainerBatch(toUpdateContainers);
                    }
                } else if (wfFail) {

                }
                break;
            case SAMPLE:
                List<ShiftSample> shiftSamples = shiftSampleService.list(new LambdaQueryWrapper<ShiftSample>()
                        .eq(ShiftSample::getShiftId, actWfShift.getShiftId()));
                if (wfSuccess) {
                    List<Long> oldTubeIds = shiftSamples.stream().map(ShiftSample::getOldTubeId).collect(Collectors.toList());
                    List<Tube> oldTubeList = tubeService.listByIds(oldTubeIds);
                    List<Long> newTubeIds = shiftSamples.stream().map(ShiftSample::getNewTubeId).collect(Collectors.toList());
                    List<Tube> newTubeList = tubeService.listByIds(newTubeIds);

                    List<Tube> toUpdateTubes = new ArrayList<>();
                    List<Container> toUpdateContainers = new ArrayList<>();
                    shiftSamples.forEach(item -> {
                        // 转移前的孔格去除样本ID
                        oldTubeList.forEach(o -> {
                            if (ObjectUtil.equal(item.getOldTubeId(), o.getTubeId())) {
                                o.setSampBackupId(0L);
                                toUpdateTubes.add(o);
                            }
                        });
                        // 转移后的孔格添加样本ID
                        newTubeList.forEach(n -> {
                            if (ObjectUtil.equal(item.getNewTubeId(), n.getTubeId())) {
                                n.setSampBackupId(item.getSampBackupId());
                                toUpdateTubes.add(n);
                            }
                        });
                        // 重新计算存储数量
                        List<Container> oldContainers = containerService.getAllParentsByChild(item.getOldContainerId());
                        List<Container> oldContainerList = oldContainers.stream().map(o -> {
                            o.setCurrentCount(o.getCurrentCount() - 1);
                            return o;
                        }).collect(Collectors.toList());
                        toUpdateContainers.addAll(oldContainerList);
                        List<Container> newContainers = containerService.getAllParentsByChild(item.getNewContainerId());
                        List<Container> newContainerList = newContainers.stream().map(n -> {
                            n.setCurrentCount(n.getCurrentCount() + 1);
                            return n;
                        }).collect(Collectors.toList());
                        toUpdateContainers.addAll(newContainerList);
                    });
                    if (CollectionUtil.isNotEmpty(toUpdateTubes)) {
                        tubeService.updateBatchById(toUpdateTubes);
                    }
                    if (CollectionUtil.isNotEmpty(toUpdateContainers)) {
                        containerService.updateContainerBatch(toUpdateContainers);
                    }
                } else if (wfFail) {

                }
                // 更新样本库存状态为正常状态
                List<Long> sampBackupIds = shiftSamples.stream().map(ShiftSample::getSampBackupId).collect(Collectors.toList());
                List<SampleBackup> sampleBackupList = sampleBackupService.listByIds(sampBackupIds);
                List<SampleBackup> sampleBackups = sampleBackupList.stream().map(item -> {
                    item.setStockState(StockState.NORMAL.getCode());
                    return item;
                }).collect(Collectors.toList());
                if (CollectionUtil.isNotEmpty(sampleBackups)) {
                    sampleBackupService.updateBatchById(sampleBackups);
                }
                break;
            default:
                break;
        }
        return this.updateById(actWfShift);
    }

}
